From 79c045ed8080e0e91962821593e6d189f7070ca7 Mon Sep 17 00:00:00 2001 From: Cosimo Cecchi Date: Wed, 23 Dec 2015 09:54:42 -0800 Subject: [PATCH] scrolledwindow: port to use a gadget --- gtk/gtkscrolledwindow.c | 2007 ++++++++++++++++++++------------------- 1 file changed, 1007 insertions(+), 1000 deletions(-) diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index 950ad3d3ad..9e1a943eb7 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -29,6 +29,7 @@ #include "gtkadjustment.h" #include "gtkadjustmentprivate.h" #include "gtkbindings.h" +#include "gtkcsscustomgadgetprivate.h" #include "gtkdnd.h" #include "gtkintl.h" #include "gtkmain.h" @@ -210,6 +211,7 @@ struct _GtkScrolledWindowPrivate GtkWidget *hscrollbar; GtkWidget *vscrollbar; + GtkCssGadget *gadget; GtkCssNode *overshoot_node[4]; GtkCssNode *undershoot_node[4]; @@ -329,6 +331,13 @@ static void gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrol static void gtk_scrolled_window_relative_allocation(GtkWidget *widget, GtkAllocation *allocation); +static void gtk_scrolled_window_inner_allocation (GtkWidget *widget, + GtkAllocation *rect); +static void gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window, + GtkWidget *scrollbar, + GtkAllocation *allocation); +static void gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow, + GtkAllocation *relative_allocation); static void gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment, gpointer data); static void gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment, @@ -1376,220 +1385,836 @@ captured_event_cb (GtkWidget *widget, return GDK_EVENT_PROPAGATE; } -static void -gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window) -{ - GtkWidget *widget = GTK_WIDGET (scrolled_window); - GtkScrolledWindowPrivate *priv; - GtkCssNode *widget_node; - GQuark classes[4] = { - g_quark_from_static_string (GTK_STYLE_CLASS_LEFT), - g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT), - g_quark_from_static_string (GTK_STYLE_CLASS_TOP), - g_quark_from_static_string (GTK_STYLE_CLASS_BOTTOM), - }; - gint i; - - scrolled_window->priv = priv = - gtk_scrolled_window_get_instance_private (scrolled_window); - - gtk_widget_set_has_window (widget, TRUE); - gtk_widget_set_can_focus (widget, TRUE); - - /* Instantiated by gtk_scrolled_window_set_[hv]adjustment - * which are both construct properties - */ - priv->hscrollbar = NULL; - priv->vscrollbar = NULL; - priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC; - priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC; - priv->hscrollbar_visible = FALSE; - priv->vscrollbar_visible = FALSE; - priv->focus_out = FALSE; - priv->window_placement = GTK_CORNER_TOP_LEFT; - priv->min_content_width = -1; - priv->min_content_height = -1; - - priv->overlay_scrolling = TRUE; - - priv->drag_gesture = gtk_gesture_drag_new (widget); - gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->drag_gesture), TRUE); - g_signal_connect_swapped (priv->drag_gesture, "drag-begin", - G_CALLBACK (scrolled_window_drag_begin_cb), - scrolled_window); - g_signal_connect_swapped (priv->drag_gesture, "drag-update", - G_CALLBACK (scrolled_window_drag_update_cb), - scrolled_window); - g_signal_connect_swapped (priv->drag_gesture, "end", - G_CALLBACK (scrolled_window_drag_end_cb), - scrolled_window); - - priv->pan_gesture = gtk_gesture_pan_new (widget, GTK_ORIENTATION_VERTICAL); - gtk_gesture_group (priv->pan_gesture, priv->drag_gesture); - gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->pan_gesture), TRUE); - - priv->swipe_gesture = gtk_gesture_swipe_new (widget); - gtk_gesture_group (priv->swipe_gesture, priv->drag_gesture); - gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->swipe_gesture), TRUE); - g_signal_connect_swapped (priv->swipe_gesture, "swipe", - G_CALLBACK (scrolled_window_swipe_cb), - scrolled_window); - priv->long_press_gesture = gtk_gesture_long_press_new (widget); - gtk_gesture_group (priv->long_press_gesture, priv->drag_gesture); - gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->long_press_gesture), TRUE); - g_signal_connect_swapped (priv->long_press_gesture, "pressed", - G_CALLBACK (scrolled_window_long_press_cb), - scrolled_window); - g_signal_connect_swapped (priv->long_press_gesture, "cancelled", - G_CALLBACK (scrolled_window_long_press_cancelled_cb), - scrolled_window); - - priv->scroll_history = g_array_new (FALSE, FALSE, sizeof (ScrollHistoryElem)); - - gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE); - gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE); - - _gtk_widget_set_captured_event_handler (widget, captured_event_cb); - - widget_node = gtk_widget_get_css_node (widget); - for (i = 0; i < 4; i++) - { - priv->overshoot_node[i] = gtk_css_node_new (); - gtk_css_node_set_name (priv->overshoot_node[i], I_("overshoot")); - gtk_css_node_add_class (priv->overshoot_node[i], classes[i]); - gtk_css_node_set_parent (priv->overshoot_node[i], widget_node); - gtk_css_node_set_state (priv->overshoot_node[i], gtk_css_node_get_state (widget_node)); - g_object_unref (priv->overshoot_node[i]); - - priv->undershoot_node[i] = gtk_css_node_new (); - gtk_css_node_set_name (priv->undershoot_node[i], I_("undershoot")); - gtk_css_node_add_class (priv->undershoot_node[i], classes[i]); - gtk_css_node_set_parent (priv->undershoot_node[i], widget_node); - gtk_css_node_set_state (priv->undershoot_node[i], gtk_css_node_get_state (widget_node)); - g_object_unref (priv->undershoot_node[i]); - } -} - -/** - * gtk_scrolled_window_new: - * @hadjustment: (allow-none): horizontal adjustment - * @vadjustment: (allow-none): vertical adjustment - * - * Creates a new scrolled window. +/* + * _gtk_scrolled_window_get_spacing: + * @scrolled_window: a scrolled window * - * The two arguments are the scrolled window’s adjustments; these will be - * shared with the scrollbars and the child widget to keep the bars in sync - * with the child. Usually you want to pass %NULL for the adjustments, which - * will cause the scrolled window to create them for you. + * Gets the spacing between the scrolled window’s scrollbars and + * the scrolled widget. Used by GtkCombo * - * Returns: a new scrolled window + * Returns: the spacing, in pixels. */ -GtkWidget* -gtk_scrolled_window_new (GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment) +static gint +_gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window) { - GtkWidget *scrolled_window; + GtkScrolledWindowClass *class; - if (hadjustment) - g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL); + g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0); - if (vadjustment) - g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL); + class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window); - scrolled_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW, - "hadjustment", hadjustment, - "vadjustment", vadjustment, - NULL); + if (class->scrollbar_spacing >= 0) + return class->scrollbar_spacing; + else + { + gint scrollbar_spacing; - return scrolled_window; + gtk_widget_style_get (GTK_WIDGET (scrolled_window), + "scrollbar-spacing", &scrollbar_spacing, + NULL); + + return scrollbar_spacing; + } } -/** - * gtk_scrolled_window_set_hadjustment: - * @scrolled_window: a #GtkScrolledWindow - * @hadjustment: horizontal scroll adjustment - * - * Sets the #GtkAdjustment for the horizontal scrollbar. - */ -void -gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window, - GtkAdjustment *hadjustment) +static void +gtk_scrolled_window_allocate (GtkCssGadget *gadget, + const GtkAllocation *allocation, + int baseline, + GtkAllocation *out_clip, + gpointer data) { - GtkScrolledWindowPrivate *priv; + GtkWidget *widget = gtk_css_gadget_get_owner (gadget); + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget); + GtkScrolledWindowPrivate *priv = scrolled_window->priv; GtkBin *bin; + GtkAllocation relative_allocation; + GtkAllocation child_allocation; GtkWidget *child; - - g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window)); - if (hadjustment) - g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment)); - else - hadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL); + gint sb_spacing; + gint sb_width; + gint sb_height; bin = GTK_BIN (scrolled_window); - priv = scrolled_window->priv; - - if (!priv->hscrollbar) - { - priv->hscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadjustment); - gtk_widget_set_parent (priv->hscrollbar, GTK_WIDGET (scrolled_window)); - g_object_ref (priv->hscrollbar); - gtk_widget_show (priv->hscrollbar); - update_scrollbar_positions (scrolled_window); - } - else - { - GtkAdjustment *old_adjustment; + /* Get possible scrollbar dimensions */ + sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window); + gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL); + gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL); - old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)); - if (old_adjustment == hadjustment) - return; + if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS) + priv->hscrollbar_visible = TRUE; + else if (priv->hscrollbar_policy == GTK_POLICY_NEVER || + priv->hscrollbar_policy == GTK_POLICY_EXTERNAL) + priv->hscrollbar_visible = FALSE; - g_signal_handlers_disconnect_by_func (old_adjustment, - gtk_scrolled_window_adjustment_changed, - scrolled_window); - gtk_adjustment_enable_animation (old_adjustment, NULL, 0); - gtk_range_set_adjustment (GTK_RANGE (priv->hscrollbar), hadjustment); - } - hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)); - g_signal_connect (hadjustment, - "changed", - G_CALLBACK (gtk_scrolled_window_adjustment_changed), - scrolled_window); - g_signal_connect (hadjustment, - "value-changed", - G_CALLBACK (gtk_scrolled_window_adjustment_value_changed), - scrolled_window); - gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window); - gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window); + if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS) + priv->vscrollbar_visible = TRUE; + else if (priv->vscrollbar_policy == GTK_POLICY_NEVER || + priv->vscrollbar_policy == GTK_POLICY_EXTERNAL) + priv->vscrollbar_visible = FALSE; child = gtk_bin_get_child (bin); - if (GTK_IS_SCROLLABLE (child)) - gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (child), hadjustment); + if (child && gtk_widget_get_visible (child)) + { + gint child_scroll_width; + gint child_scroll_height; + GtkScrollablePolicy hscroll_policy; + GtkScrollablePolicy vscroll_policy; + gboolean previous_hvis; + gboolean previous_vvis; + guint count = 0; - if (gtk_scrolled_window_should_animate (scrolled_window)) - gtk_adjustment_enable_animation (hadjustment, gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window)), ANIMATION_DURATION); - g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_HADJUSTMENT]); -} + hscroll_policy = GTK_IS_SCROLLABLE (child) + ? gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (child)) + : GTK_SCROLL_MINIMUM; + vscroll_policy = GTK_IS_SCROLLABLE (child) + ? gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (child)) + : GTK_SCROLL_MINIMUM; -/** - * gtk_scrolled_window_set_vadjustment: - * @scrolled_window: a #GtkScrolledWindow - * @vadjustment: vertical scroll adjustment - * - * Sets the #GtkAdjustment for the vertical scrollbar. - */ -void -gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window, - GtkAdjustment *vadjustment) -{ - GtkScrolledWindowPrivate *priv; - GtkBin *bin; - GtkWidget *child; + /* Determine scrollbar visibility first via hfw apis */ + if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH) + { + if (hscroll_policy == GTK_SCROLL_MINIMUM) + gtk_widget_get_preferred_width (child, &child_scroll_width, NULL); + else + gtk_widget_get_preferred_width (child, NULL, &child_scroll_width); - g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window)); - if (vadjustment) - g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment)); + if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC) + { + /* First try without a vertical scrollbar if the content will fit the height + * given the extra width of the scrollbar */ + if (vscroll_policy == GTK_SCROLL_MINIMUM) + gtk_widget_get_preferred_height_for_width (child, + MAX (allocation->width, child_scroll_width), + &child_scroll_height, NULL); + else + gtk_widget_get_preferred_height_for_width (child, + MAX (allocation->width, child_scroll_width), + NULL, &child_scroll_height); + + if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) + { + /* Does the content height fit the allocation height ? */ + priv->vscrollbar_visible = child_scroll_height > allocation->height; + + /* Does the content width fit the allocation with minus a possible scrollbar ? */ + priv->hscrollbar_visible = + child_scroll_width > allocation->width - + (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0); + + /* Now that we've guessed the hscrollbar, does the content height fit + * the possible new allocation height ? + */ + priv->vscrollbar_visible = + child_scroll_height > allocation->height - + (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0); + + /* Now that we've guessed the vscrollbar, does the content width fit + * the possible new allocation width ? + */ + priv->hscrollbar_visible = + child_scroll_width > allocation->width - + (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0); + } + else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */ + { + priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy); + priv->vscrollbar_visible = child_scroll_height > allocation->height - + (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0); + } + } + else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */ + { + priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy); + + if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) + priv->hscrollbar_visible = + child_scroll_width > allocation->width - + (priv->vscrollbar_visible && !priv->use_indicators ? 0 : sb_width + sb_spacing); + else + priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy); + } + } + else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */ + { + if (vscroll_policy == GTK_SCROLL_MINIMUM) + gtk_widget_get_preferred_height (child, &child_scroll_height, NULL); + else + gtk_widget_get_preferred_height (child, NULL, &child_scroll_height); + + if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) + { + /* First try without a horizontal scrollbar if the content will fit the width + * given the extra height of the scrollbar */ + if (hscroll_policy == GTK_SCROLL_MINIMUM) + gtk_widget_get_preferred_width_for_height (child, + MAX (allocation->height, child_scroll_height), + &child_scroll_width, NULL); + else + gtk_widget_get_preferred_width_for_height (child, + MAX (allocation->height, child_scroll_height), + NULL, &child_scroll_width); + + if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC) + { + /* Does the content width fit the allocation width ? */ + priv->hscrollbar_visible = child_scroll_width > allocation->width; + + /* Does the content height fit the allocation with minus a possible scrollbar ? */ + priv->vscrollbar_visible = + child_scroll_height > allocation->height - + (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0); + + /* Now that we've guessed the vscrollbar, does the content width fit + * the possible new allocation width ? + */ + priv->hscrollbar_visible = + child_scroll_width > allocation->width - + (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0); + + /* Now that we've guessed the hscrollbar, does the content height fit + * the possible new allocation height ? + */ + priv->vscrollbar_visible = + child_scroll_height > allocation->height - + (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0); + } + else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */ + { + priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy); + priv->hscrollbar_visible = child_scroll_width > allocation->width - + (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0); + } + } + else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */ + { + priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy); + + if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC) + priv->vscrollbar_visible = + child_scroll_height > allocation->height - + (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0); + else + priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy); + } + } + + /* Now after guessing scrollbar visibility; fall back on the allocation loop which + * observes the adjustments to detect scrollbar visibility and also avoids + * infinite recursion + */ + do + { + previous_hvis = priv->hscrollbar_visible; + previous_vvis = priv->vscrollbar_visible; + gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation); + + /* Explicitly force scrollbar visibility checks. + * + * Since we make a guess above, the child might not decide to update the adjustments + * if they logically did not change since the last configuration + */ + if (priv->hscrollbar) + gtk_scrolled_window_adjustment_changed + (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)), scrolled_window); + + if (priv->vscrollbar) + gtk_scrolled_window_adjustment_changed + (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)), scrolled_window); + + /* If, after the first iteration, the hscrollbar and the + * vscrollbar flip visiblity... or if one of the scrollbars flip + * on each itteration indefinitly/infinitely, then we just need both + * at this size. + */ + if ((count && + previous_hvis != priv->hscrollbar_visible && + previous_vvis != priv->vscrollbar_visible) || count > 3) + { + priv->hscrollbar_visible = TRUE; + priv->vscrollbar_visible = TRUE; + + gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation); + + break; + } + + count++; + } + while (previous_hvis != priv->hscrollbar_visible || + previous_vvis != priv->vscrollbar_visible); + } + else + { + priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS; + priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS; + gtk_scrolled_window_relative_allocation (widget, &relative_allocation); + } + + gtk_widget_set_child_visible (priv->hscrollbar, priv->hscrollbar_visible); + if (priv->hscrollbar_visible) + { + gtk_scrolled_window_allocate_scrollbar (scrolled_window, + priv->hscrollbar, + &child_allocation); + if (priv->use_indicators) + { + gdk_window_move_resize (priv->hindicator.window, + child_allocation.x, + child_allocation.y, + child_allocation.width, + child_allocation.height); + child_allocation.x = 0; + child_allocation.y = 0; + } + gtk_widget_size_allocate (priv->hscrollbar, &child_allocation); + } + + gtk_widget_set_child_visible (priv->vscrollbar, priv->vscrollbar_visible); + if (priv->vscrollbar_visible) + { + gtk_scrolled_window_allocate_scrollbar (scrolled_window, + priv->vscrollbar, + &child_allocation); + if (priv->use_indicators) + { + gdk_window_move_resize (priv->vindicator.window, + child_allocation.x, + child_allocation.y, + child_allocation.width, + child_allocation.height); + child_allocation.x = 0; + child_allocation.y = 0; + } + gtk_widget_size_allocate (priv->vscrollbar, &child_allocation); + } + + gtk_scrolled_window_check_attach_pan_gesture (scrolled_window); +} + +static void +gtk_scrolled_window_measure (GtkCssGadget *gadget, + GtkOrientation orientation, + int for_size, + int *minimum_size, + int *natural_size, + int *minimum_baseline, + int *natural_baseline, + gpointer data) +{ + GtkWidget *widget = gtk_css_gadget_get_owner (gadget); + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget); + GtkScrolledWindowPrivate *priv = scrolled_window->priv; + GtkBin *bin = GTK_BIN (scrolled_window); + gint extra_width; + gint extra_height; + gint scrollbar_spacing; + GtkRequisition hscrollbar_requisition; + GtkRequisition vscrollbar_requisition; + GtkRequisition minimum_req, natural_req; + GtkWidget *child; + gint min_child_size, nat_child_size; + + scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window); + + extra_width = 0; + extra_height = 0; + minimum_req.width = 0; + minimum_req.height = 0; + natural_req.width = 0; + natural_req.height = 0; + + gtk_widget_get_preferred_size (priv->hscrollbar, + &hscrollbar_requisition, NULL); + gtk_widget_get_preferred_size (priv->vscrollbar, + &vscrollbar_requisition, NULL); + + child = gtk_bin_get_child (bin); + if (child && gtk_widget_get_visible (child)) + { + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + gtk_widget_get_preferred_width (child, + &min_child_size, + &nat_child_size); + + if (priv->hscrollbar_policy == GTK_POLICY_NEVER) + { + minimum_req.width += min_child_size; + natural_req.width += nat_child_size; + } + else + { + if (priv->min_content_width >= 0) + { + minimum_req.width = MAX (minimum_req.width, priv->min_content_width); + natural_req.width = MAX (natural_req.width, priv->min_content_width); + extra_width = -1; + } + else if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators) + { + minimum_req.width += vscrollbar_requisition.width; + natural_req.width += vscrollbar_requisition.width; + } + } + } + else /* GTK_ORIENTATION_VERTICAL */ + { + gtk_widget_get_preferred_height (child, + &min_child_size, + &nat_child_size); + + if (priv->vscrollbar_policy == GTK_POLICY_NEVER) + { + minimum_req.height += min_child_size; + natural_req.height += nat_child_size; + } + else + { + if (priv->min_content_height >= 0) + { + minimum_req.height = MAX (minimum_req.height, priv->min_content_height); + natural_req.height = MAX (natural_req.height, priv->min_content_height); + extra_height = -1; + } + else if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators) + { + minimum_req.height += vscrollbar_requisition.height; + natural_req.height += vscrollbar_requisition.height; + } + } + } + } + + if (policy_may_be_visible (priv->hscrollbar_policy) && !priv->use_indicators) + { + minimum_req.width = MAX (minimum_req.width, hscrollbar_requisition.width); + natural_req.width = MAX (natural_req.width, hscrollbar_requisition.width); + if (!extra_height || priv->hscrollbar_policy == GTK_POLICY_ALWAYS) + extra_height = scrollbar_spacing + hscrollbar_requisition.height; + } + + if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators) + { + minimum_req.height = MAX (minimum_req.height, vscrollbar_requisition.height); + natural_req.height = MAX (natural_req.height, vscrollbar_requisition.height); + if (!extra_width || priv->vscrollbar_policy == GTK_POLICY_ALWAYS) + extra_width = scrollbar_spacing + vscrollbar_requisition.width; + } + + minimum_req.width += MAX (0, extra_width); + minimum_req.height += MAX (0, extra_height); + natural_req.width += MAX (0, extra_width); + natural_req.height += MAX (0, extra_height); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + *minimum_size = minimum_req.width; + *natural_size = natural_req.width; + } + else + { + *minimum_size = minimum_req.height; + *natural_size = natural_req.height; + } +} + +static void +gtk_scrolled_window_draw_scrollbars_junction (GtkScrolledWindow *scrolled_window, + cairo_t *cr) +{ + GtkScrolledWindowPrivate *priv = scrolled_window->priv; + GtkWidget *widget = GTK_WIDGET (scrolled_window); + GtkAllocation content_allocation, hscr_allocation, vscr_allocation; + GtkStyleContext *context; + GdkRectangle junction_rect; + gboolean is_rtl; + + is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; + gtk_widget_get_allocation (GTK_WIDGET (priv->hscrollbar), &hscr_allocation); + gtk_widget_get_allocation (GTK_WIDGET (priv->vscrollbar), &vscr_allocation); + gtk_css_gadget_get_content_allocation (priv->gadget, &content_allocation, + NULL); + + junction_rect.x = content_allocation.x; + junction_rect.y = content_allocation.y; + junction_rect.width = vscr_allocation.width; + junction_rect.height = hscr_allocation.height; + + if ((is_rtl && + (priv->window_placement == GTK_CORNER_TOP_RIGHT || + priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) || + (!is_rtl && + (priv->window_placement == GTK_CORNER_TOP_LEFT || + priv->window_placement == GTK_CORNER_BOTTOM_LEFT))) + junction_rect.x += hscr_allocation.width; + + if (priv->window_placement == GTK_CORNER_TOP_LEFT || + priv->window_placement == GTK_CORNER_TOP_RIGHT) + junction_rect.y += vscr_allocation.height; + + context = gtk_widget_get_style_context (widget); + gtk_style_context_save_named (context, "junction"); + + gtk_render_background (context, cr, + junction_rect.x, junction_rect.y, + junction_rect.width, junction_rect.height); + gtk_render_frame (context, cr, + junction_rect.x, junction_rect.y, + junction_rect.width, junction_rect.height); + + gtk_style_context_restore (context); +} + +static void +gtk_scrolled_window_draw_overshoot (GtkScrolledWindow *scrolled_window, + cairo_t *cr) +{ + GtkScrolledWindowPrivate *priv = scrolled_window->priv; + GtkWidget *widget = GTK_WIDGET (scrolled_window); + gint overshoot_x, overshoot_y; + GtkStyleContext *context; + GdkRectangle rect; + + if (!_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y)) + return; + + context = gtk_widget_get_style_context (widget); + gtk_scrolled_window_inner_allocation (widget, &rect); + + overshoot_x = CLAMP (overshoot_x, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE); + overshoot_y = CLAMP (overshoot_y, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE); + + if (overshoot_x > 0) + { + gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_RIGHT]); + gtk_render_background (context, cr, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height); + gtk_render_frame (context, cr, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height); + gtk_style_context_restore (context); + } + else if (overshoot_x < 0) + { + gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_LEFT]); + gtk_render_background (context, cr, rect.x, rect.y, -overshoot_x, rect.height); + gtk_render_frame (context, cr, rect.x, rect.y, -overshoot_x, rect.height); + gtk_style_context_restore (context); + } + + if (overshoot_y > 0) + { + gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_BOTTOM]); + gtk_render_background (context, cr, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y); + gtk_render_frame (context, cr, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y); + gtk_style_context_restore (context); + } + else if (overshoot_y < 0) + { + gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_TOP]); + gtk_render_background (context, cr, rect.x, rect.y, rect.width, -overshoot_y); + gtk_render_frame (context, cr, rect.x, rect.y, rect.width, -overshoot_y); + gtk_style_context_restore (context); + } +} + +static void +gtk_scrolled_window_draw_undershoot (GtkScrolledWindow *scrolled_window, + cairo_t *cr) +{ + GtkScrolledWindowPrivate *priv = scrolled_window->priv; + GtkWidget *widget = GTK_WIDGET (scrolled_window); + GtkStyleContext *context; + GdkRectangle rect; + GtkAdjustment *adj; + + context = gtk_widget_get_style_context (widget); + gtk_scrolled_window_inner_allocation (widget, &rect); + + adj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)); + if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj)) + { + gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_RIGHT]); + gtk_render_background (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height); + gtk_render_frame (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height); + + gtk_style_context_restore (context); + } + if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj)) + { + gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_LEFT]); + gtk_render_background (context, cr, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height); + gtk_render_frame (context, cr, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height); + gtk_style_context_restore (context); + } + + adj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)); + if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj)) + { + gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_BOTTOM]); + gtk_render_background (context, cr, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE); + gtk_render_frame (context, cr, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE); + gtk_style_context_restore (context); + } + if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj)) + { + gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_TOP]); + gtk_render_background (context, cr, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE); + gtk_render_frame (context, cr, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE); + gtk_style_context_restore (context); + } +} + +static gboolean +gtk_scrolled_window_render (GtkCssGadget *gadget, + cairo_t *cr, + int x, + int y, + int width, + int height, + gpointer data) +{ + GtkWidget *widget = gtk_css_gadget_get_owner (gadget); + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget); + GtkScrolledWindowPrivate *priv = scrolled_window->priv; + + if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget))) + { + if (priv->hscrollbar_visible && + priv->vscrollbar_visible) + gtk_scrolled_window_draw_scrollbars_junction (scrolled_window, cr); + } + + GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->draw (widget, cr); + + if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget))) + { + gtk_scrolled_window_draw_undershoot (scrolled_window, cr); + gtk_scrolled_window_draw_overshoot (scrolled_window, cr); + } + + return FALSE; +} + +static void +gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window) +{ + GtkWidget *widget = GTK_WIDGET (scrolled_window); + GtkScrolledWindowPrivate *priv; + GtkCssNode *widget_node; + GQuark classes[4] = { + g_quark_from_static_string (GTK_STYLE_CLASS_LEFT), + g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT), + g_quark_from_static_string (GTK_STYLE_CLASS_TOP), + g_quark_from_static_string (GTK_STYLE_CLASS_BOTTOM), + }; + gint i; + + scrolled_window->priv = priv = + gtk_scrolled_window_get_instance_private (scrolled_window); + + gtk_widget_set_has_window (widget, TRUE); + gtk_widget_set_can_focus (widget, TRUE); + + /* Instantiated by gtk_scrolled_window_set_[hv]adjustment + * which are both construct properties + */ + priv->hscrollbar = NULL; + priv->vscrollbar = NULL; + priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC; + priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC; + priv->hscrollbar_visible = FALSE; + priv->vscrollbar_visible = FALSE; + priv->focus_out = FALSE; + priv->window_placement = GTK_CORNER_TOP_LEFT; + priv->min_content_width = -1; + priv->min_content_height = -1; + + priv->overlay_scrolling = TRUE; + + priv->drag_gesture = gtk_gesture_drag_new (widget); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->drag_gesture), TRUE); + g_signal_connect_swapped (priv->drag_gesture, "drag-begin", + G_CALLBACK (scrolled_window_drag_begin_cb), + scrolled_window); + g_signal_connect_swapped (priv->drag_gesture, "drag-update", + G_CALLBACK (scrolled_window_drag_update_cb), + scrolled_window); + g_signal_connect_swapped (priv->drag_gesture, "end", + G_CALLBACK (scrolled_window_drag_end_cb), + scrolled_window); + + priv->pan_gesture = gtk_gesture_pan_new (widget, GTK_ORIENTATION_VERTICAL); + gtk_gesture_group (priv->pan_gesture, priv->drag_gesture); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->pan_gesture), TRUE); + + priv->swipe_gesture = gtk_gesture_swipe_new (widget); + gtk_gesture_group (priv->swipe_gesture, priv->drag_gesture); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->swipe_gesture), TRUE); + g_signal_connect_swapped (priv->swipe_gesture, "swipe", + G_CALLBACK (scrolled_window_swipe_cb), + scrolled_window); + priv->long_press_gesture = gtk_gesture_long_press_new (widget); + gtk_gesture_group (priv->long_press_gesture, priv->drag_gesture); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->long_press_gesture), TRUE); + g_signal_connect_swapped (priv->long_press_gesture, "pressed", + G_CALLBACK (scrolled_window_long_press_cb), + scrolled_window); + g_signal_connect_swapped (priv->long_press_gesture, "cancelled", + G_CALLBACK (scrolled_window_long_press_cancelled_cb), + scrolled_window); + + priv->scroll_history = g_array_new (FALSE, FALSE, sizeof (ScrollHistoryElem)); + + gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE); + gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE); + + _gtk_widget_set_captured_event_handler (widget, captured_event_cb); + + widget_node = gtk_widget_get_css_node (widget); + priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node, + widget, + gtk_scrolled_window_measure, + gtk_scrolled_window_allocate, + gtk_scrolled_window_render, + NULL, NULL); + for (i = 0; i < 4; i++) + { + priv->overshoot_node[i] = gtk_css_node_new (); + gtk_css_node_set_name (priv->overshoot_node[i], I_("overshoot")); + gtk_css_node_add_class (priv->overshoot_node[i], classes[i]); + gtk_css_node_set_parent (priv->overshoot_node[i], widget_node); + gtk_css_node_set_state (priv->overshoot_node[i], gtk_css_node_get_state (widget_node)); + g_object_unref (priv->overshoot_node[i]); + + priv->undershoot_node[i] = gtk_css_node_new (); + gtk_css_node_set_name (priv->undershoot_node[i], I_("undershoot")); + gtk_css_node_add_class (priv->undershoot_node[i], classes[i]); + gtk_css_node_set_parent (priv->undershoot_node[i], widget_node); + gtk_css_node_set_state (priv->undershoot_node[i], gtk_css_node_get_state (widget_node)); + g_object_unref (priv->undershoot_node[i]); + } +} + +/** + * gtk_scrolled_window_new: + * @hadjustment: (allow-none): horizontal adjustment + * @vadjustment: (allow-none): vertical adjustment + * + * Creates a new scrolled window. + * + * The two arguments are the scrolled window’s adjustments; these will be + * shared with the scrollbars and the child widget to keep the bars in sync + * with the child. Usually you want to pass %NULL for the adjustments, which + * will cause the scrolled window to create them for you. + * + * Returns: a new scrolled window + */ +GtkWidget* +gtk_scrolled_window_new (GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment) +{ + GtkWidget *scrolled_window; + + if (hadjustment) + g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL); + + if (vadjustment) + g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL); + + scrolled_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW, + "hadjustment", hadjustment, + "vadjustment", vadjustment, + NULL); + + return scrolled_window; +} + +/** + * gtk_scrolled_window_set_hadjustment: + * @scrolled_window: a #GtkScrolledWindow + * @hadjustment: horizontal scroll adjustment + * + * Sets the #GtkAdjustment for the horizontal scrollbar. + */ +void +gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window, + GtkAdjustment *hadjustment) +{ + GtkScrolledWindowPrivate *priv; + GtkBin *bin; + GtkWidget *child; + + g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window)); + if (hadjustment) + g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment)); + else + hadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL); + + bin = GTK_BIN (scrolled_window); + priv = scrolled_window->priv; + + if (!priv->hscrollbar) + { + priv->hscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadjustment); + + gtk_widget_set_parent (priv->hscrollbar, GTK_WIDGET (scrolled_window)); + g_object_ref (priv->hscrollbar); + gtk_widget_show (priv->hscrollbar); + update_scrollbar_positions (scrolled_window); + } + else + { + GtkAdjustment *old_adjustment; + + old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)); + if (old_adjustment == hadjustment) + return; + + g_signal_handlers_disconnect_by_func (old_adjustment, + gtk_scrolled_window_adjustment_changed, + scrolled_window); + gtk_adjustment_enable_animation (old_adjustment, NULL, 0); + gtk_range_set_adjustment (GTK_RANGE (priv->hscrollbar), hadjustment); + } + hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)); + g_signal_connect (hadjustment, + "changed", + G_CALLBACK (gtk_scrolled_window_adjustment_changed), + scrolled_window); + g_signal_connect (hadjustment, + "value-changed", + G_CALLBACK (gtk_scrolled_window_adjustment_value_changed), + scrolled_window); + gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window); + gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window); + + child = gtk_bin_get_child (bin); + if (GTK_IS_SCROLLABLE (child)) + gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (child), hadjustment); + + if (gtk_scrolled_window_should_animate (scrolled_window)) + gtk_adjustment_enable_animation (hadjustment, gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window)), ANIMATION_DURATION); + g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_HADJUSTMENT]); +} + +/** + * gtk_scrolled_window_set_vadjustment: + * @scrolled_window: a #GtkScrolledWindow + * @vadjustment: vertical scroll adjustment + * + * Sets the #GtkAdjustment for the vertical scrollbar. + */ +void +gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window, + GtkAdjustment *vadjustment) +{ + GtkScrolledWindowPrivate *priv; + GtkBin *bin; + GtkWidget *child; + + g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window)); + if (vadjustment) + g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment)); else vadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL); @@ -2077,6 +2702,7 @@ gtk_scrolled_window_destroy (GtkWidget *widget) g_clear_object (&priv->drag_gesture); g_clear_object (&priv->swipe_gesture); g_clear_object (&priv->long_press_gesture); + g_clear_object (&priv->gadget); g_clear_pointer (&priv->scroll_history, (GDestroyNotify) g_array_unref); GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->destroy (widget); @@ -2196,183 +2822,23 @@ gtk_scrolled_window_get_property (GObject *object, } } -static void -gtk_scrolled_window_draw_scrollbars_junction (GtkScrolledWindow *scrolled_window, - cairo_t *cr) -{ - GtkScrolledWindowPrivate *priv = scrolled_window->priv; - GtkWidget *widget = GTK_WIDGET (scrolled_window); - GtkAllocation hscr_allocation, vscr_allocation; - GtkStyleContext *context; - GdkRectangle junction_rect; - gboolean is_rtl; - - is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; - gtk_widget_get_allocation (GTK_WIDGET (priv->hscrollbar), &hscr_allocation); - gtk_widget_get_allocation (GTK_WIDGET (priv->vscrollbar), &vscr_allocation); - - context = gtk_widget_get_style_context (widget); - - if (priv->shadow_type != GTK_SHADOW_NONE) - { - GtkStateFlags state; - GtkBorder padding, border; - - state = gtk_style_context_get_state (context); - - gtk_style_context_get_padding (context, state, &padding); - gtk_style_context_get_border (context, state, &border); - - junction_rect.x = padding.left + border.left; - junction_rect.y = padding.top + border.top; - } - else - { - junction_rect.x = 0; - junction_rect.y = 0; - } - - junction_rect.width = vscr_allocation.width; - junction_rect.height = hscr_allocation.height; - - if ((is_rtl && - (priv->window_placement == GTK_CORNER_TOP_RIGHT || - priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) || - (!is_rtl && - (priv->window_placement == GTK_CORNER_TOP_LEFT || - priv->window_placement == GTK_CORNER_BOTTOM_LEFT))) - junction_rect.x += hscr_allocation.width; - - if (priv->window_placement == GTK_CORNER_TOP_LEFT || - priv->window_placement == GTK_CORNER_TOP_RIGHT) - junction_rect.y += vscr_allocation.height; - - gtk_style_context_save_named (context, "junction"); - - gtk_render_background (context, cr, - junction_rect.x, junction_rect.y, - junction_rect.width, junction_rect.height); - gtk_render_frame (context, cr, - junction_rect.x, junction_rect.y, - junction_rect.width, junction_rect.height); - - gtk_style_context_restore (context); -} - -static void -gtk_scrolled_window_inner_allocation (GtkWidget *widget, - GtkAllocation *rect) -{ - GtkWidget *child; - GtkBorder border = { 0 }; - - gtk_scrolled_window_relative_allocation (widget, rect); - - child = gtk_bin_get_child (GTK_BIN (widget)); - if (GTK_IS_SCROLLABLE (child) && - gtk_scrollable_get_border (GTK_SCROLLABLE (child), &border)) - { - rect->x += border.left; - rect->y += border.top; - rect->width -= border.left + border.right; - rect->height -= border.top + border.bottom; - } -} - -static void -gtk_scrolled_window_draw_overshoot (GtkScrolledWindow *scrolled_window, - cairo_t *cr) -{ - GtkScrolledWindowPrivate *priv = scrolled_window->priv; - GtkWidget *widget = GTK_WIDGET (scrolled_window); - gint overshoot_x, overshoot_y; - GtkStyleContext *context; - GdkRectangle rect; - - if (!_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y)) - return; - - context = gtk_widget_get_style_context (widget); - gtk_scrolled_window_inner_allocation (widget, &rect); - - overshoot_x = CLAMP (overshoot_x, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE); - overshoot_y = CLAMP (overshoot_y, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE); - - if (overshoot_x > 0) - { - gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_RIGHT]); - gtk_render_background (context, cr, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height); - gtk_render_frame (context, cr, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height); - gtk_style_context_restore (context); - } - else if (overshoot_x < 0) - { - gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_LEFT]); - gtk_render_background (context, cr, rect.x, rect.y, -overshoot_x, rect.height); - gtk_render_frame (context, cr, rect.x, rect.y, -overshoot_x, rect.height); - gtk_style_context_restore (context); - } - - if (overshoot_y > 0) - { - gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_BOTTOM]); - gtk_render_background (context, cr, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y); - gtk_render_frame (context, cr, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y); - gtk_style_context_restore (context); - } - else if (overshoot_y < 0) - { - gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_TOP]); - gtk_render_background (context, cr, rect.x, rect.y, rect.width, -overshoot_y); - gtk_render_frame (context, cr, rect.x, rect.y, rect.width, -overshoot_y); - gtk_style_context_restore (context); - } -} - -static void -gtk_scrolled_window_draw_undershoot (GtkScrolledWindow *scrolled_window, - cairo_t *cr) -{ - GtkScrolledWindowPrivate *priv = scrolled_window->priv; - GtkWidget *widget = GTK_WIDGET (scrolled_window); - GtkStyleContext *context; - GdkRectangle rect; - GtkAdjustment *adj; - - context = gtk_widget_get_style_context (widget); - gtk_scrolled_window_inner_allocation (widget, &rect); - - adj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)); - if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj)) - { - gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_RIGHT]); - gtk_render_background (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height); - gtk_render_frame (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height); +static void +gtk_scrolled_window_inner_allocation (GtkWidget *widget, + GtkAllocation *rect) +{ + GtkWidget *child; + GtkBorder border = { 0 }; - gtk_style_context_restore (context); - } - if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj)) - { - gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_LEFT]); - gtk_render_background (context, cr, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height); - gtk_render_frame (context, cr, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height); - gtk_style_context_restore (context); - } + gtk_scrolled_window_relative_allocation (widget, rect); - adj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)); - if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj)) - { - gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_BOTTOM]); - gtk_render_background (context, cr, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE); - gtk_render_frame (context, cr, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE); - gtk_style_context_restore (context); - } - if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj)) + child = gtk_bin_get_child (GTK_BIN (widget)); + if (GTK_IS_SCROLLABLE (child) && + gtk_scrollable_get_border (GTK_SCROLLABLE (child), &border)) { - gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_TOP]); - gtk_render_background (context, cr, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE); - gtk_render_frame (context, cr, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE); - gtk_style_context_restore (context); + rect->x += border.left; + rect->y += border.top; + rect->width -= border.left + border.right; + rect->height -= border.top + border.bottom; } } @@ -2382,33 +2848,8 @@ gtk_scrolled_window_draw (GtkWidget *widget, { GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget); GtkScrolledWindowPrivate *priv = scrolled_window->priv; - GtkStyleContext *context; - - if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget))) - { - context = gtk_widget_get_style_context (widget); - - gtk_render_background (context, cr, - 0, 0, - gtk_widget_get_allocated_width (widget), - gtk_widget_get_allocated_height (widget)); - gtk_render_frame (context, cr, - 0, 0, - gtk_widget_get_allocated_width (widget), - gtk_widget_get_allocated_height (widget)); - - if (priv->hscrollbar_visible && - priv->vscrollbar_visible) - gtk_scrolled_window_draw_scrollbars_junction (scrolled_window, cr); - } - GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->draw (widget, cr); - - if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget))) - { - gtk_scrolled_window_draw_undershoot (scrolled_window, cr); - gtk_scrolled_window_draw_overshoot (scrolled_window, cr); - } + gtk_css_gadget_draw (priv->gadget, cr); return FALSE; } @@ -2571,7 +3012,7 @@ static void gtk_scrolled_window_relative_allocation (GtkWidget *widget, GtkAllocation *allocation) { - GtkAllocation widget_allocation; + GtkAllocation content_allocation; GtkScrolledWindow *scrolled_window; GtkScrolledWindowPrivate *priv; gint sb_spacing; @@ -2589,474 +3030,204 @@ gtk_scrolled_window_relative_allocation (GtkWidget *widget, gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL); gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL); - gtk_widget_get_allocation (widget, &widget_allocation); - - allocation->x = 0; - allocation->y = 0; - allocation->width = widget_allocation.width; - allocation->height = widget_allocation.height; - - /* Subtract some things from our available allocation size */ - if (priv->shadow_type != GTK_SHADOW_NONE) - { - GtkStyleContext *context; - GtkStateFlags state; - GtkBorder padding, border; - - context = gtk_widget_get_style_context (widget); - state = gtk_style_context_get_state (context); - - gtk_style_context_get_border (context, state, &border); - gtk_style_context_get_padding (context, state, &padding); - - allocation->x += padding.left + border.left; - allocation->y += padding.top + border.top; - allocation->width = MAX (1, allocation->width - (padding.left + border.left + padding.right + border.right)); - allocation->height = MAX (1, allocation->height - (padding.top + border.top + padding.bottom + border.bottom)); - } - - if (priv->vscrollbar_visible && !priv->use_indicators) - { - gboolean is_rtl; - - is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; - - if ((!is_rtl && - (priv->window_placement == GTK_CORNER_TOP_RIGHT || - priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) || - (is_rtl && - (priv->window_placement == GTK_CORNER_TOP_LEFT || - priv->window_placement == GTK_CORNER_BOTTOM_LEFT))) - allocation->x += (sb_width + sb_spacing); - - allocation->width = MAX (1, allocation->width - (sb_width + sb_spacing)); - } - - if (priv->hscrollbar_visible && !priv->use_indicators) - { - - if (priv->window_placement == GTK_CORNER_BOTTOM_LEFT || - priv->window_placement == GTK_CORNER_BOTTOM_RIGHT) - allocation->y += (sb_height + sb_spacing); - - allocation->height = MAX (1, allocation->height - (sb_height + sb_spacing)); - } -} - -static gboolean -_gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window, - gint *overshoot_x, - gint *overshoot_y) -{ - GtkScrolledWindowPrivate *priv = scrolled_window->priv; - GtkAdjustment *vadjustment, *hadjustment; - gdouble lower, upper, x, y; - - /* Vertical overshoot */ - vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)); - lower = gtk_adjustment_get_lower (vadjustment); - upper = gtk_adjustment_get_upper (vadjustment) - - gtk_adjustment_get_page_size (vadjustment); - - if (priv->unclamped_vadj_value < lower) - y = priv->unclamped_vadj_value - lower; - else if (priv->unclamped_vadj_value > upper) - y = priv->unclamped_vadj_value - upper; - else - y = 0; - - /* Horizontal overshoot */ - hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)); - lower = gtk_adjustment_get_lower (hadjustment); - upper = gtk_adjustment_get_upper (hadjustment) - - gtk_adjustment_get_page_size (hadjustment); - - if (priv->unclamped_hadj_value < lower) - x = priv->unclamped_hadj_value - lower; - else if (priv->unclamped_hadj_value > upper) - x = priv->unclamped_hadj_value - upper; - else - x = 0; - - if (overshoot_x) - *overshoot_x = x; - - if (overshoot_y) - *overshoot_y = y; - - return (x != 0 || y != 0); -} - -static void -gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow, - GtkAllocation *relative_allocation) -{ - GtkWidget *widget = GTK_WIDGET (swindow), *child; - GtkAllocation child_allocation; - - child = gtk_bin_get_child (GTK_BIN (widget)); - - gtk_scrolled_window_relative_allocation (widget, relative_allocation); - - child_allocation.x = relative_allocation->x; - child_allocation.y = relative_allocation->y; - child_allocation.width = relative_allocation->width; - child_allocation.height = relative_allocation->height; - - gtk_widget_size_allocate (child, &child_allocation); -} - -static void -gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window, - GtkWidget *scrollbar, - GtkAllocation *allocation) -{ - GtkAllocation child_allocation, content_allocation; - GtkWidget *widget = GTK_WIDGET (scrolled_window); - gint sb_spacing, sb_height, sb_width; - GtkScrolledWindowPrivate *priv; - - priv = scrolled_window->priv; - - gtk_scrolled_window_inner_allocation (widget, &content_allocation); - sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window); - gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL); - gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL); - - if (scrollbar == priv->hscrollbar) - { - child_allocation.x = content_allocation.x; - - if (priv->window_placement == GTK_CORNER_TOP_LEFT || - priv->window_placement == GTK_CORNER_TOP_RIGHT) - { - if (priv->use_indicators) - child_allocation.y = content_allocation.y + content_allocation.height - sb_height; - else - child_allocation.y = content_allocation.y + content_allocation.height + sb_spacing; - } - else - { - if (priv->use_indicators) - child_allocation.y = content_allocation.y; - else - child_allocation.y = content_allocation.y - sb_spacing - sb_height; - } - - child_allocation.width = content_allocation.width; - child_allocation.height = sb_height; - } - else if (scrollbar == priv->vscrollbar) - { - if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && - (priv->window_placement == GTK_CORNER_TOP_RIGHT || - priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) || - (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR && - (priv->window_placement == GTK_CORNER_TOP_LEFT || - priv->window_placement == GTK_CORNER_BOTTOM_LEFT))) - { - if (priv->use_indicators) - child_allocation.x = content_allocation.x + content_allocation.width - sb_width; - else - child_allocation.x = content_allocation.x + content_allocation.width + sb_spacing; - } - else - { - if (priv->use_indicators) - child_allocation.x = content_allocation.x; - else - child_allocation.x = content_allocation.x - sb_spacing - sb_width; - } - - child_allocation.y = content_allocation.y; - child_allocation.width = sb_width; - child_allocation.height = content_allocation.height; - } - - *allocation = child_allocation; -} - -static void -gtk_scrolled_window_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) -{ - GtkScrolledWindow *scrolled_window; - GtkScrolledWindowPrivate *priv; - GtkBin *bin; - GtkAllocation relative_allocation; - GtkAllocation child_allocation; - GtkWidget *child; - gint sb_spacing; - gint sb_width; - gint sb_height; - - scrolled_window = GTK_SCROLLED_WINDOW (widget); - bin = GTK_BIN (scrolled_window); - priv = scrolled_window->priv; - - if (gtk_widget_get_realized (widget)) - { - gdk_window_move_resize (gtk_widget_get_window (widget), - allocation->x, allocation->y, - allocation->width, allocation->height); - } - - /* Get possible scrollbar dimensions */ - sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window); - gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL); - gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL); - - gtk_widget_set_allocation (widget, allocation); - - if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS) - priv->hscrollbar_visible = TRUE; - else if (priv->hscrollbar_policy == GTK_POLICY_NEVER || - priv->hscrollbar_policy == GTK_POLICY_EXTERNAL) - priv->hscrollbar_visible = FALSE; + gtk_css_gadget_get_content_allocation (priv->gadget, &content_allocation, + NULL); - if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS) - priv->vscrollbar_visible = TRUE; - else if (priv->vscrollbar_policy == GTK_POLICY_NEVER || - priv->vscrollbar_policy == GTK_POLICY_EXTERNAL) - priv->vscrollbar_visible = FALSE; + allocation->x = content_allocation.x; + allocation->y = content_allocation.y; + allocation->width = content_allocation.width; + allocation->height = content_allocation.height; - child = gtk_bin_get_child (bin); - if (child && gtk_widget_get_visible (child)) + /* Subtract some things from our available allocation size */ + if (priv->vscrollbar_visible && !priv->use_indicators) { - gint child_scroll_width; - gint child_scroll_height; - GtkScrollablePolicy hscroll_policy; - GtkScrollablePolicy vscroll_policy; - gboolean previous_hvis; - gboolean previous_vvis; - guint count = 0; + gboolean is_rtl; - hscroll_policy = GTK_IS_SCROLLABLE (child) - ? gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (child)) - : GTK_SCROLL_MINIMUM; - vscroll_policy = GTK_IS_SCROLLABLE (child) - ? gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (child)) - : GTK_SCROLL_MINIMUM; + is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; - /* Determine scrollbar visibility first via hfw apis */ - if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH) - { - if (hscroll_policy == GTK_SCROLL_MINIMUM) - gtk_widget_get_preferred_width (child, &child_scroll_width, NULL); - else - gtk_widget_get_preferred_width (child, NULL, &child_scroll_width); + if ((!is_rtl && + (priv->window_placement == GTK_CORNER_TOP_RIGHT || + priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) || + (is_rtl && + (priv->window_placement == GTK_CORNER_TOP_LEFT || + priv->window_placement == GTK_CORNER_BOTTOM_LEFT))) + allocation->x += (sb_width + sb_spacing); - if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC) - { - /* First try without a vertical scrollbar if the content will fit the height - * given the extra width of the scrollbar */ - if (vscroll_policy == GTK_SCROLL_MINIMUM) - gtk_widget_get_preferred_height_for_width (child, - MAX (allocation->width, child_scroll_width), - &child_scroll_height, NULL); - else - gtk_widget_get_preferred_height_for_width (child, - MAX (allocation->width, child_scroll_width), - NULL, &child_scroll_height); + allocation->width = MAX (1, allocation->width - (sb_width + sb_spacing)); + } - if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) - { - /* Does the content height fit the allocation height ? */ - priv->vscrollbar_visible = child_scroll_height > allocation->height; + if (priv->hscrollbar_visible && !priv->use_indicators) + { - /* Does the content width fit the allocation with minus a possible scrollbar ? */ - priv->hscrollbar_visible = - child_scroll_width > allocation->width - - (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0); + if (priv->window_placement == GTK_CORNER_BOTTOM_LEFT || + priv->window_placement == GTK_CORNER_BOTTOM_RIGHT) + allocation->y += (sb_height + sb_spacing); - /* Now that we've guessed the hscrollbar, does the content height fit - * the possible new allocation height ? - */ - priv->vscrollbar_visible = - child_scroll_height > allocation->height - - (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0); + allocation->height = MAX (1, allocation->height - (sb_height + sb_spacing)); + } +} - /* Now that we've guessed the vscrollbar, does the content width fit - * the possible new allocation width ? - */ - priv->hscrollbar_visible = - child_scroll_width > allocation->width - - (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0); - } - else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */ - { - priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy); - priv->vscrollbar_visible = child_scroll_height > allocation->height - - (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0); - } - } - else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */ - { - priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy); +static gboolean +_gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window, + gint *overshoot_x, + gint *overshoot_y) +{ + GtkScrolledWindowPrivate *priv = scrolled_window->priv; + GtkAdjustment *vadjustment, *hadjustment; + gdouble lower, upper, x, y; - if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) - priv->hscrollbar_visible = - child_scroll_width > allocation->width - - (priv->vscrollbar_visible && !priv->use_indicators ? 0 : sb_width + sb_spacing); - else - priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy); - } - } - else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */ - { - if (vscroll_policy == GTK_SCROLL_MINIMUM) - gtk_widget_get_preferred_height (child, &child_scroll_height, NULL); - else - gtk_widget_get_preferred_height (child, NULL, &child_scroll_height); + /* Vertical overshoot */ + vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)); + lower = gtk_adjustment_get_lower (vadjustment); + upper = gtk_adjustment_get_upper (vadjustment) - + gtk_adjustment_get_page_size (vadjustment); - if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) - { - /* First try without a horizontal scrollbar if the content will fit the width - * given the extra height of the scrollbar */ - if (hscroll_policy == GTK_SCROLL_MINIMUM) - gtk_widget_get_preferred_width_for_height (child, - MAX (allocation->height, child_scroll_height), - &child_scroll_width, NULL); - else - gtk_widget_get_preferred_width_for_height (child, - MAX (allocation->height, child_scroll_height), - NULL, &child_scroll_width); + if (priv->unclamped_vadj_value < lower) + y = priv->unclamped_vadj_value - lower; + else if (priv->unclamped_vadj_value > upper) + y = priv->unclamped_vadj_value - upper; + else + y = 0; - if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC) - { - /* Does the content width fit the allocation width ? */ - priv->hscrollbar_visible = child_scroll_width > allocation->width; + /* Horizontal overshoot */ + hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)); + lower = gtk_adjustment_get_lower (hadjustment); + upper = gtk_adjustment_get_upper (hadjustment) - + gtk_adjustment_get_page_size (hadjustment); - /* Does the content height fit the allocation with minus a possible scrollbar ? */ - priv->vscrollbar_visible = - child_scroll_height > allocation->height - - (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0); + if (priv->unclamped_hadj_value < lower) + x = priv->unclamped_hadj_value - lower; + else if (priv->unclamped_hadj_value > upper) + x = priv->unclamped_hadj_value - upper; + else + x = 0; - /* Now that we've guessed the vscrollbar, does the content width fit - * the possible new allocation width ? - */ - priv->hscrollbar_visible = - child_scroll_width > allocation->width - - (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0); + if (overshoot_x) + *overshoot_x = x; - /* Now that we've guessed the hscrollbar, does the content height fit - * the possible new allocation height ? - */ - priv->vscrollbar_visible = - child_scroll_height > allocation->height - - (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0); - } - else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */ - { - priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy); - priv->hscrollbar_visible = child_scroll_width > allocation->width - - (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0); - } - } - else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */ - { - priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy); + if (overshoot_y) + *overshoot_y = y; - if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC) - priv->vscrollbar_visible = - child_scroll_height > allocation->height - - (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0); - else - priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy); - } - } + return (x != 0 || y != 0); +} - /* Now after guessing scrollbar visibility; fall back on the allocation loop which - * observes the adjustments to detect scrollbar visibility and also avoids - * infinite recursion - */ - do - { - previous_hvis = priv->hscrollbar_visible; - previous_vvis = priv->vscrollbar_visible; - gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation); +static void +gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow, + GtkAllocation *relative_allocation) +{ + GtkWidget *widget = GTK_WIDGET (swindow), *child; + GtkAllocation child_allocation; - /* Explicitly force scrollbar visibility checks. - * - * Since we make a guess above, the child might not decide to update the adjustments - * if they logically did not change since the last configuration - */ - if (priv->hscrollbar) - gtk_scrolled_window_adjustment_changed - (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)), scrolled_window); + child = gtk_bin_get_child (GTK_BIN (widget)); - if (priv->vscrollbar) - gtk_scrolled_window_adjustment_changed - (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)), scrolled_window); + gtk_scrolled_window_relative_allocation (widget, relative_allocation); - /* If, after the first iteration, the hscrollbar and the - * vscrollbar flip visiblity... or if one of the scrollbars flip - * on each itteration indefinitly/infinitely, then we just need both - * at this size. - */ - if ((count && - previous_hvis != priv->hscrollbar_visible && - previous_vvis != priv->vscrollbar_visible) || count > 3) - { - priv->hscrollbar_visible = TRUE; - priv->vscrollbar_visible = TRUE; + child_allocation.x = relative_allocation->x; + child_allocation.y = relative_allocation->y; + child_allocation.width = relative_allocation->width; + child_allocation.height = relative_allocation->height; - gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation); + gtk_widget_size_allocate (child, &child_allocation); +} - break; - } +static void +gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window, + GtkWidget *scrollbar, + GtkAllocation *allocation) +{ + GtkAllocation child_allocation, content_allocation; + GtkWidget *widget = GTK_WIDGET (scrolled_window); + gint sb_spacing, sb_height, sb_width; + GtkScrolledWindowPrivate *priv; + + priv = scrolled_window->priv; + + gtk_scrolled_window_inner_allocation (widget, &content_allocation); + sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window); + gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL); + gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL); - count++; - } - while (previous_hvis != priv->hscrollbar_visible || - previous_vvis != priv->vscrollbar_visible); - } - else + if (scrollbar == priv->hscrollbar) { - priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS; - priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS; - gtk_scrolled_window_relative_allocation (widget, &relative_allocation); - } + child_allocation.x = content_allocation.x; - gtk_widget_set_child_visible (priv->hscrollbar, priv->hscrollbar_visible); - if (priv->hscrollbar_visible) - { - gtk_scrolled_window_allocate_scrollbar (scrolled_window, - priv->hscrollbar, - &child_allocation); - if (priv->use_indicators) + if (priv->window_placement == GTK_CORNER_TOP_LEFT || + priv->window_placement == GTK_CORNER_TOP_RIGHT) { - gdk_window_move_resize (priv->hindicator.window, - child_allocation.x, - child_allocation.y, - child_allocation.width, - child_allocation.height); - child_allocation.x = 0; - child_allocation.y = 0; + if (priv->use_indicators) + child_allocation.y = content_allocation.y + content_allocation.height - sb_height; + else + child_allocation.y = content_allocation.y + content_allocation.height + sb_spacing; + } + else + { + if (priv->use_indicators) + child_allocation.y = content_allocation.y; + else + child_allocation.y = content_allocation.y - sb_spacing - sb_height; } - gtk_widget_size_allocate (priv->hscrollbar, &child_allocation); - } - gtk_widget_set_child_visible (priv->vscrollbar, priv->vscrollbar_visible); - if (priv->vscrollbar_visible) + child_allocation.width = content_allocation.width; + child_allocation.height = sb_height; + } + else if (scrollbar == priv->vscrollbar) { - gtk_scrolled_window_allocate_scrollbar (scrolled_window, - priv->vscrollbar, - &child_allocation); - if (priv->use_indicators) + if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && + (priv->window_placement == GTK_CORNER_TOP_RIGHT || + priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) || + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR && + (priv->window_placement == GTK_CORNER_TOP_LEFT || + priv->window_placement == GTK_CORNER_BOTTOM_LEFT))) { - gdk_window_move_resize (priv->vindicator.window, - child_allocation.x, - child_allocation.y, - child_allocation.width, - child_allocation.height); - child_allocation.x = 0; - child_allocation.y = 0; + if (priv->use_indicators) + child_allocation.x = content_allocation.x + content_allocation.width - sb_width; + else + child_allocation.x = content_allocation.x + content_allocation.width + sb_spacing; } - gtk_widget_size_allocate (priv->vscrollbar, &child_allocation); + else + { + if (priv->use_indicators) + child_allocation.x = content_allocation.x; + else + child_allocation.x = content_allocation.x - sb_spacing - sb_width; + } + + child_allocation.y = content_allocation.y; + child_allocation.width = sb_width; + child_allocation.height = content_allocation.height; } - gtk_scrolled_window_check_attach_pan_gesture (scrolled_window); + *allocation = child_allocation; +} + +static void +gtk_scrolled_window_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkScrolledWindow *scrolled_window; + GtkScrolledWindowPrivate *priv; + GtkAllocation clip, content_allocation; + + scrolled_window = GTK_SCROLLED_WINDOW (widget); + priv = scrolled_window->priv; + + gtk_widget_set_allocation (widget, allocation); + + if (gtk_widget_get_realized (widget)) + gdk_window_move_resize (gtk_widget_get_window (widget), + allocation->x, allocation->y, + allocation->width, allocation->height); + + content_allocation = *allocation; + content_allocation.x = content_allocation.y = 0; + gtk_css_gadget_allocate (priv->gadget, + &content_allocation, + gtk_widget_get_allocated_baseline (widget), + &clip); + + clip.x += allocation->x; + clip.y += allocation->y; + gtk_widget_set_clip (widget, &clip); } static gboolean @@ -3657,184 +3828,16 @@ gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window, gtk_container_add (GTK_CONTAINER (viewport), child); } -/* - * _gtk_scrolled_window_get_spacing: - * @scrolled_window: a scrolled window - * - * Gets the spacing between the scrolled window’s scrollbars and - * the scrolled widget. Used by GtkCombo - * - * Returns: the spacing, in pixels. - */ -static gint -_gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window) -{ - GtkScrolledWindowClass *class; - - g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0); - - class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window); - - if (class->scrollbar_spacing >= 0) - return class->scrollbar_spacing; - else - { - gint scrollbar_spacing; - - gtk_widget_style_get (GTK_WIDGET (scrolled_window), - "scrollbar-spacing", &scrollbar_spacing, - NULL); - - return scrollbar_spacing; - } -} - -static void -gtk_scrolled_window_get_preferred_size (GtkWidget *widget, - GtkOrientation orientation, - gint *minimum_size, - gint *natural_size) -{ - GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget); - GtkScrolledWindowPrivate *priv = scrolled_window->priv; - GtkBin *bin = GTK_BIN (scrolled_window); - gint extra_width; - gint extra_height; - gint scrollbar_spacing; - GtkRequisition hscrollbar_requisition; - GtkRequisition vscrollbar_requisition; - GtkRequisition minimum_req, natural_req; - GtkWidget *child; - gint min_child_size, nat_child_size; - - scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window); - - extra_width = 0; - extra_height = 0; - minimum_req.width = 0; - minimum_req.height = 0; - natural_req.width = 0; - natural_req.height = 0; - - gtk_widget_get_preferred_size (priv->hscrollbar, - &hscrollbar_requisition, NULL); - gtk_widget_get_preferred_size (priv->vscrollbar, - &vscrollbar_requisition, NULL); - - child = gtk_bin_get_child (bin); - if (child && gtk_widget_get_visible (child)) - { - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - gtk_widget_get_preferred_width (child, - &min_child_size, - &nat_child_size); - - if (priv->hscrollbar_policy == GTK_POLICY_NEVER) - { - minimum_req.width += min_child_size; - natural_req.width += nat_child_size; - } - else - { - if (priv->min_content_width >= 0) - { - minimum_req.width = MAX (minimum_req.width, priv->min_content_width); - natural_req.width = MAX (natural_req.width, priv->min_content_width); - extra_width = -1; - } - else if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators) - { - minimum_req.width += vscrollbar_requisition.width; - natural_req.width += vscrollbar_requisition.width; - } - } - } - else /* GTK_ORIENTATION_VERTICAL */ - { - gtk_widget_get_preferred_height (child, - &min_child_size, - &nat_child_size); - - if (priv->vscrollbar_policy == GTK_POLICY_NEVER) - { - minimum_req.height += min_child_size; - natural_req.height += nat_child_size; - } - else - { - if (priv->min_content_height >= 0) - { - minimum_req.height = MAX (minimum_req.height, priv->min_content_height); - natural_req.height = MAX (natural_req.height, priv->min_content_height); - extra_height = -1; - } - else if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators) - { - minimum_req.height += vscrollbar_requisition.height; - natural_req.height += vscrollbar_requisition.height; - } - } - } - } - - if (policy_may_be_visible (priv->hscrollbar_policy) && !priv->use_indicators) - { - minimum_req.width = MAX (minimum_req.width, hscrollbar_requisition.width); - natural_req.width = MAX (natural_req.width, hscrollbar_requisition.width); - if (!extra_height || priv->hscrollbar_policy == GTK_POLICY_ALWAYS) - extra_height = scrollbar_spacing + hscrollbar_requisition.height; - } - - if (policy_may_be_visible (priv->vscrollbar_policy) && !priv->use_indicators) - { - minimum_req.height = MAX (minimum_req.height, vscrollbar_requisition.height); - natural_req.height = MAX (natural_req.height, vscrollbar_requisition.height); - if (!extra_width || priv->vscrollbar_policy == GTK_POLICY_ALWAYS) - extra_width = scrollbar_spacing + vscrollbar_requisition.width; - } - - minimum_req.width += MAX (0, extra_width); - minimum_req.height += MAX (0, extra_height); - natural_req.width += MAX (0, extra_width); - natural_req.height += MAX (0, extra_height); - - if (priv->shadow_type != GTK_SHADOW_NONE) - { - GtkStyleContext *context; - GtkStateFlags state; - GtkBorder padding, border; - - context = gtk_widget_get_style_context (GTK_WIDGET (widget)); - state = gtk_style_context_get_state (context); - - gtk_style_context_get_padding (context, state, &padding); - gtk_style_context_get_border (context, state, &border); - - minimum_req.width += padding.left + padding.right + border.left + border.right; - minimum_req.height += padding.top + padding.bottom + border.top + border.bottom; - natural_req.width += padding.left + padding.right + border.left + border.right; - natural_req.height += padding.top + padding.bottom + border.top + border.bottom; - } - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - *minimum_size = minimum_req.width; - *natural_size = natural_req.width; - } - else - { - *minimum_size = minimum_req.height; - *natural_size = natural_req.height; - } -} - static void gtk_scrolled_window_get_preferred_width (GtkWidget *widget, gint *minimum_size, gint *natural_size) { - gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size); + gtk_css_gadget_get_preferred_size (GTK_SCROLLED_WINDOW (widget)->priv->gadget, + GTK_ORIENTATION_HORIZONTAL, + -1, + minimum_size, natural_size, + NULL, NULL); } static void @@ -3842,7 +3845,11 @@ gtk_scrolled_window_get_preferred_height (GtkWidget *widget, gint *minimum_size, gint *natural_size) { - gtk_scrolled_window_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size); + gtk_css_gadget_get_preferred_size (GTK_SCROLLED_WINDOW (widget)->priv->gadget, + GTK_ORIENTATION_VERTICAL, + -1, + minimum_size, natural_size, + NULL, NULL); } static void -- 2.30.2